#include <EEPROM.h>
#include <MIDI.h>
#include "Arp.h"
#include "Chord.h"
#include "Echo.h"
#include "Random.h"

#define ArpMode 0
#define ChordMode 1
#define EchoMode 2
#define RandomMode 3

MIDI midi;
Arp arp(&midi);
Chord chord(&midi);
Echo echo(&midi);
Random rndm(&midi);
int mode = 0;
int channelIn = 0;
int channelOut = 0;

void setup() { // TODO: get stuff from memory
  pinMode(13, OUTPUT);
  midi.begin();
  //arp.readFromMemory(); // TODO: default template or first time setup function
  readDataFromMemory();  
}

void loop() {
  digitalWrite(13, LOW);
  
  if (midi.read() == true) { // TODO: use channels
    byte status = midi.getStatus();
    switch (midi.getType(status)) {
      case System:
        if (status == 0xFA) { // Start
          switch (mode) {
            case ArpMode:
              arp.handleStart();
          }
        }
        else if (status == 0xF8) { // Sync
          switch (mode) {
            case ArpMode:
              arp.sync();
            break;
            
            case EchoMode:
              echo.sync();
          }  
        }
        else if (status == 0xF0) { // System Exclusive
          delayMicroseconds(4250); // USB needs a longer delay between bytes
          if (Serial.read() == 0x7F) { // Manufacturer ID
            //digitalWrite(13, HIGH);
            delayMicroseconds(4250);
           // digitalWrite(13, LOW);
            byte eox = getDataFromSerial(); 
            if (eox == 0xF7) {
              digitalWrite(13, HIGH);
              delay(3000);
              writeDataToMemory();
            }
          }
          //else { 
            // TODO: Forward SysEx, ID, data and EOX (0xF7)
          //{
        }
        else {
          forwardMessage();
        }
      break;
      
      case NoteOn:
        //forwardMessage(); // Debug
        /*if (midi.getData1() == 36) {
          digitalWrite(13, HIGH);
          delay(1000);
          digitalWrite(13, LOW);
        }*/ 
        switch (mode) {
          case ArpMode:
            arp.handleNoteOn();
          break;
          
          case ChordMode:
            chord.handleNoteOnOff();
          break;
          
          case EchoMode:
            echo.handleNoteOn();
          break;
          
          case RandomMode:
            rndm.handleNoteOn();
        } 
      break;
      
      case NoteOff:
        //forwardMessage(); // Debug
        switch (mode) {
          case ArpMode:
            arp.handleNoteOff();
          break;
          
          case ChordMode:
            chord.handleNoteOnOff();
          break;
          
          case EchoMode:
            echo.handleNoteOff();
          break;
          
          case RandomMode:
            rndm.handleNoteOff();
        }
    }
  }
  //byte b = 0xF0;
  //midi.send(10);
  //Serial.print("hello");
  //Serial.write(b);
  //delay(1000);
}

void forwardMessage() {
  switch (midi.getSize()) {
    case 1:
      midi.send(midi.getStatus());
    break;
    
    case 2:
      midi.send(midi.getStatus(), midi.getData1());
    break;
    
    case 3:
      midi.send(midi.getStatus(), midi.getData1(), midi.getData2());
  }
}

byte getDataFromSerial() {
  mode = Serial.read();
  if (mode == 3) {
    //digitalWrite(13, HIGH);
    delay(3000); // without this delay stuff isn't received properly !?
  }
  delayMicroseconds(4250);
  channelIn = Serial.read();
  delayMicroseconds(4250);
  channelOut = Serial.read();
  delayMicroseconds(4250);
  rndm.readFromSerial();
  echo.readFromSerial();
  chord.readFromSerial();
  arp.readFromSerial();
  return Serial.read();
}

void readDataFromMemory() {
  mode = EEPROM.read(0);
  channelIn = EEPROM.read(1);
  channelOut = EEPROM.read(2);
  rndm.readFromMemory(3);
  echo.readFromMemory(7);
  chord.readFromMemory(10);
  arp.readFromMemory(11 + EEPROM.read(10));
}

void writeDataToMemory() {
  EEPROM.write(0, mode);
  EEPROM.write(1, channelIn);
  EEPROM.write(2, channelOut);
  rndm.writeToMemory(3);
  echo.writeToMemory(7);
  chord.writeToMemory(10);
  arp.writeToMemory(11 + EEPROM.read(10));
}

void writeDataToSerial() {
  Serial.write(mode);
  Serial.write(channelIn);
  Serial.write(channelOut);
  rndm.writeToSerial();
  echo.writeToSerial();
  chord.writeToSerial();
  arp.writeToSerial();
}
